/*
	Entity.cpp

	Contains code for the entity class

	Author: Aidan Goddard 10/8/14
*/

#include<Windows.h>
#include<strsafe.h>
#include<exception>

#include"Dispatch.h"
#include"EngineState.h"
#include"Error.h"
#include"Engine.h"
#include"SnowstormXEntity.h"
#include"SnowstormXEngineVars.h"

//////////////////////////////////////////////////
// externals
extern "C" void ASMEntry(void);
extern "C" void ASMTaskSwitch(uint64_t *current_task_stack, uint64_t *new_task_stack);

//////////////////////////////////////////////////
// constructor & destructor

Snowstorm::Entity::Entity()
{
	// setup control vars
	this->executing = false;
	this->start_params = NULL;
	this->wait_time = 0;
	this->stack = NULL;

	// temporary friendly name
	this->name = L"Entity";
	this->sub_name = L"base";
	this->id_1 = 0;
	this->id_2 = 0;

	// finished
	return;
}

Snowstorm::Entity::~Entity()
{
	// delete the stack
	if(this->stack != NULL)
	{
		delete this->stack;
	}

	// finished
	return;
}

//////////////////////////////////////////////////
// default action

uint64_t Snowstorm::Entity::Action(void *start_params)
{
	// finished
	return 0;
}

//////////////////////////////////////////////////
// initialise execution data

void Snowstorm::Entity::MakeExecutionReady(void *start_params)
{
	this->InitExecutionData(start_params);
	return;
}

void Snowstorm::Entity::InitExecutionData(void *start_params)
{
	// save start params and set the executing state to true
	this->start_params = start_params;
	this->executing = true;
	this->wait_time = 1;

	// set up the initial registers and stack state
	if(this->stack)
		delete this->stack;
	this->stack = new uint64_t[DEFAULT_STACK_SIZE];
	if(!this->stack)	
	{
		wchar_t msg[512];
		StringCbPrintf(msg, 512, L"ERROR: could not allocate entity stack space\nEntity: %s %s %u (0x%x) %u (0x%x)", 
			this->name,
			this->sub_name,
			this->id_1,
			this->id_1,
			this->id_2,
			this->id_2);
		Snowstorm::Error::SignalError(L"Entity Stack Error", msg);
	}
	this->stack_size = DEFAULT_STACK_SIZE;
	this->stack[0] = STACK_MAGIC;

	// set initial stack state
	this->stack[this->stack_size - 1] = (uint64_t)ASMEntry;	// bottom of stack (last item popped)
	this->stack[this->stack_size - 2] = (uint64_t)0;						// R12
	this->stack[this->stack_size - 3] = (uint64_t)0;						// R13
	this->stack[this->stack_size - 4] = (uint64_t)0;						// R14
	this->stack[this->stack_size - 5] = (uint64_t)0;						// R15
	this->stack[this->stack_size - 6] = (uint64_t)this->start_params;		// RDI
	this->stack[this->stack_size - 7] = (uint64_t)this;						// RSI
	this->stack[this->stack_size - 8] = (uint64_t)0;						// RBX
	this->stack[this->stack_size - 9] = (uint64_t)0;						// RBP
	
	this->stack[this->stack_size - 10] = (uint64_t)0;						// XMM6
	this->stack[this->stack_size - 11] = (uint64_t)0;						// XMM6
	this->stack[this->stack_size - 12] = (uint64_t)0;						// XMM7
	this->stack[this->stack_size - 13] = (uint64_t)0;						// XMM7
	this->stack[this->stack_size - 14] = (uint64_t)0;						// XMM8
	this->stack[this->stack_size - 15] = (uint64_t)0;						// XMM8
	this->stack[this->stack_size - 16] = (uint64_t)0;						// XMM9
	this->stack[this->stack_size - 17] = (uint64_t)0;						// XMM9
	this->stack[this->stack_size - 18] = (uint64_t)0;						// XMM10
	this->stack[this->stack_size - 19] = (uint64_t)0;						// XMM10
	this->stack[this->stack_size - 20] = (uint64_t)0;						// XMM11
	this->stack[this->stack_size - 21] = (uint64_t)0;						// XMM11
	this->stack[this->stack_size - 22] = (uint64_t)0;						// XMM12
	this->stack[this->stack_size - 23] = (uint64_t)0;						// XMM12
	this->stack[this->stack_size - 24] = (uint64_t)0;						// XMM13
	this->stack[this->stack_size - 25] = (uint64_t)0;						// XMM13
	this->stack[this->stack_size - 26] = (uint64_t)0;						// XMM14
	this->stack[this->stack_size - 27] = (uint64_t)0;						// XMM14
	this->stack[this->stack_size - 28] = (uint64_t)0;						// XMM15
	this->stack[this->stack_size - 29] = (uint64_t)0;						// XMM15

	this->stack[this->stack_size - 30] = (uint64_t)0x1f80;					// MXCSR
	
	// set stack pointers
	this->entity_stack = (uint64_t)&(this->stack[this->stack_size - 30]);
	this->thread_stack = 0;

	// set engine vars pointers
	this->ev = Snowstorm::Engine::ev;

	return;
}

//////////////////////////////////////////////////
// set & get name and IDs

void Snowstorm::Entity::SetName(wchar_t *name, wchar_t *sub_name, uint32_t id_1, uint32_t id_2)
{
	// set the names
	this->name = name;
	this->sub_name = sub_name;
	this->id_1 = id_1;
	this->id_2 = id_2;

	// finished
	return;
}

//////////////////////////////////////////////////
// resume function & associated

extern "C" void ActionIntermediate(Snowstorm::Entity *entity, void *start_params)
{
	// call the action
	try
	{
		entity->SetActionReturnCode(entity->Action(start_params));
	}
	catch(std::exception *e)
	{
		wchar_t msg[512];
		StringCbPrintf(msg, 512, L"ERROR: exception in entity: %s : %s %u (0x%x) %u (0x%x):\n%s", 
			entity->name,
			entity->sub_name,
			entity->id_1,
			entity->id_1,
			entity->id_2,
			entity->id_2,
			e->what());
		Snowstorm::Error::SignalError(L"Stack Overflow", msg);
	}
	// action returned, set entity to not executing
	entity->executing = false;

	// now switch back to main thread
	uint64_t *entity_stack;
	uint64_t *thread_stack;
	entity->GetStacks(&entity_stack, &thread_stack);
	ASMTaskSwitch(entity_stack, thread_stack);

	return;
}

void Snowstorm::Entity::SetThreadID(uint32_t thread_ID)
{
	this->SNOWSTORM_ENGINE_TID = thread_ID;
	return;
}

void Snowstorm::Entity::SetActionReturnCode(uint64_t code)
{
	this->action_return_code = code;
	return;
}

uint64_t Snowstorm::Entity::GetActionReturnCode()
{
	return this->action_return_code;
}

void Snowstorm::Entity::GetStacks(uint64_t **entity_stack, uint64_t **thread_stack)
{
	*entity_stack = &this->entity_stack;
	*thread_stack = &this->thread_stack;
	return;
}

void Snowstorm::Entity::Resume(uint32_t thread_ID)
{
	// check if this entity is executing
	if(!this->executing)
	{
		// this entity is not executing
		return;
	}

	// check if entity is waiting on a frame, or a timer
	if(this->wait_time < 0)
	{
		// is waiting on a timer
		// increase it by the previous frame time
		this->wait_time += ev->frame_time;

		// check it is ready to resume
		if(this->wait_time < 0)
		{
			// not yet ready
			return;
		}
	}
	else
	{
		// is waiting on a frame
		// decrease it by a frame
		this->wait_time -= 1.0;

		// check it is ready to resume
		if(this->wait_time > 0)
		{
			return;
		}
	}

	// check the stack magic value is not corrupt (helps to catch stack overruns)
	if(this->stack[0] != STACK_MAGIC)
	{
		wchar_t msg[512];
		StringCbPrintf(msg, 512, L"ERROR: stack overflow in entity: %s : %s %u (0x%x) %u (0x%x)", 
			this->name,
			this->sub_name,
			this->id_1,
			this->id_1,
			this->id_2,
			this->id_2);
		Snowstorm::Error::SignalError(L"Stack Overflow", msg);
	}

	// set the entity's thread ID
	this->SNOWSTORM_ENGINE_TID = thread_ID;

	// now resume the task (if we got this far, it's ready)
	ASMTaskSwitch(&this->thread_stack, &this->entity_stack);


	return;
}

//////////////////////////////////////////////////
// wait function

void Snowstorm::Entity::Wait(double wait_time)
{
	// check for zero wait condition
	if(wait_time == 0)
	{
		// return immediately
		return;
	}

	// set the wait time
	this->wait_time = wait_time;

	// now set this task to wait
	ASMTaskSwitch(&this->entity_stack, &this->thread_stack);

	return;
}



/////////////////////////////////////////////////////////////////////////////////////////////////
// entity creation/deletion

SNOWSTORMXDLL_API void CreatePersistentEntity(uint32_t SNOWSTORM_ENGINE_TID, Snowstorm::Entity *entity)
{
	// check the entity exists
	if(!entity)
	{
		return;
	}

	// signal creation
	Snowstorm::Dispatch::InsertNewPersistentEntity(SNOWSTORM_ENGINE_TID, entity);
	Snowstorm::EngineState::engine_state.cmd_create_persistent_entity = true;
	return;
}

SNOWSTORMXDLL_API void DeletePersistentEntity(uint32_t SNOWSTORM_ENGINE_TID, Snowstorm::Entity *entity)
{
	// check the entity pointer
	if(!entity)
	{
		return;
	}

	// signal creation
	Snowstorm::Dispatch::RemovePersistentEntity(SNOWSTORM_ENGINE_TID, entity);
	Snowstorm::EngineState::engine_state.cmd_delete_persistent_entity = true;
	return;
}

